#include "stdafx.h"
#include <MyControls/BezierManip.h>
#include <MyControls/BezierEditor.h>
#include <MyControls/Action.h>

namespace My {

// DrawFirstPoint

DrawFirstPoint::DrawFirstPoint(BezierEditor & editor, CPoint p) :
	m_Editor(editor)
{
	m_Node.position = m_Editor.GetBench().ClientToBench(p);
	m_Node.control[0] = m_Node.control[1] = m_Node.position;
	m_Node.type = bnFlat;
	m_Editor.GetBench().Invalidate();
	m_Editor.GetBench().SetCapture();
}

void DrawFirstPoint::Manipulate(UINT nFlags, CPoint p)
{
	point<float> fp = m_Editor.GetBench().ClientToBench(p);
	m_Node.control[1] = fp;
	m_Node.control[0] = m_Node.position * 2 - fp;
	m_Node.type = bnSymmetrical;
	m_Editor.GetBench().Invalidate();
}

void DrawFirstPoint::Apply(UINT nFlags, CPoint p)
{
	m_Editor.GetBezierOwner()->DeselectAll();
	Size i = m_Editor.GetBezierOwner()->CreateBezier(m_Node);
	m_Editor.GetBezierOwner()->GetBezier(i).Select(0);
	ReleaseCapture();
	m_Editor.GetBench().Invalidate();
}

void DrawFirstPoint::PaintRect(CDC * pDC, const CRect & rClip)
{
	CPen blue(PS_SOLID, 1, RGB(0, 0, 255));
	CPen * pOld = pDC->SelectObject(&blue);

	m_Editor.DrawNode(pDC, m_Node, true, RGB(0, 0, 255));
	m_Editor.DrawHandle(pDC, m_Node.position, m_Node.control[0], RGB(0, 0, 255));
	m_Editor.DrawHandle(pDC, m_Node.position, m_Node.control[1], RGB(0, 0, 255));

	pDC->SelectObject(pOld);
}

// DrawNextPoint

DrawNextPoint::DrawNextPoint(BezierEditor & editor, Bezier & curve, Size dir, CPoint p) :
	m_Editor(editor),
	m_Curve(curve),
	m_Dir(dir)
{
	m_Nodes[m_Dir].position = m_Editor.GetBench().ClientToBench(p);
	m_Nodes[m_Dir].control[0] = m_Nodes[m_Dir].control[1] = m_Nodes[m_Dir].position;
	m_Nodes[m_Dir].type = bnFlat;
	m_Nodes[1 - m_Dir] = m_Dir == 0 ? m_Curve.GetNode(0) : m_Curve.GetNode(m_Curve.GetNodeCount() - 1);
	m_Editor.GetBench().Invalidate();
	m_Editor.GetBench().SetCapture();
}

void DrawNextPoint::Manipulate(UINT nFlags, CPoint p)
{
	point<float> fp = m_Editor.GetBench().ClientToBench(p);
	m_Nodes[m_Dir].control[m_Dir] = fp;
	m_Nodes[m_Dir].control[1 - m_Dir] = m_Nodes[m_Dir].position * 2 - fp;
	m_Nodes[m_Dir].type = bnSymmetrical;
	m_Editor.GetBench().Invalidate();
}

void DrawNextPoint::Apply(UINT nFlags, CPoint p)
{
	// Insert new node into the active curve
	m_Curve.Deselect();
	if (m_Dir == 0) {
		m_Curve.Prepend(m_Nodes[0]);
		m_Curve.Select(0);
	} else
		m_Curve.Select(m_Curve.Append(m_Nodes[1]));
	ReleaseCapture();
	m_Editor.GetBench().Invalidate();
}

void DrawNextPoint::PaintRect(CDC * pDC, const CRect & rect)
{
	CPen blue(PS_SOLID, 1, RGB(0, 0, 255));
	CPen * pOld = pDC->SelectObject(&blue);

	POINT pp[4];
	pp[0] = m_Editor.GetBench().BenchToClient(m_Nodes[0].position);
	pp[1] = m_Editor.GetBench().BenchToClient(m_Nodes[0].control[1]);
	pp[2] = m_Editor.GetBench().BenchToClient(m_Nodes[1].control[0]);
	pp[3] = m_Editor.GetBench().BenchToClient(m_Nodes[1].position);
	pDC->PolyBezier(pp, 4);

	m_Editor.DrawNode(pDC, m_Nodes[m_Dir], true, RGB(0, 0, 255));
	m_Editor.DrawHandle(pDC, m_Nodes[m_Dir].position, m_Nodes[m_Dir].control[0], RGB(0, 0, 255));
	m_Editor.DrawHandle(pDC, m_Nodes[m_Dir].position, m_Nodes[m_Dir].control[1], RGB(0, 0, 255));

	pDC->SelectObject(pOld);
}

// DragControlPoint

DragControlPoint::DragControlPoint(BezierEditor & editor, Bezier & curve,
	Size inode,	Size icontrol, CPoint start, BezierNode * pHint) :
	m_Editor(editor),
	m_Curve(curve),
	m_NodeIndex(inode),
	m_ControlIndex(icontrol)
{
	m_Node = pHint ? * pHint : m_Curve.GetNode(m_NodeIndex);
	m_Old = m_Node.control[m_ControlIndex];
	m_Start = m_Editor.GetBench().ClientToBench(start);
	m_Editor.GetBench().SetCapture();
}

void DragControlPoint::Manipulate(UINT nFlags, CPoint p)
{
	SetNodeControlPoint(m_Node, m_ControlIndex, m_Old + 
		m_Editor.GetBench().ClientToBench(p) - m_Start);
	m_Editor.GetBench().Invalidate();
}

void DragControlPoint::Apply(UINT nFlags, CPoint p)
{
	m_Curve.SetNode(m_NodeIndex, m_Node);
	m_Editor.GetBench().Invalidate();
	ReleaseCapture();
}

void DragControlPoint::PaintRect(CDC * pDC, const CRect &)
{
	CPen blue(PS_SOLID, 1, RGB(0, 0, 255));
	CPen * pOld = pDC->SelectObject(&blue);

	if (m_Curve.IsClosed() || m_NodeIndex > 0) {
		// Draw the spline back
		BezierNode n = m_Curve.GetNode(Prev(m_Curve, m_NodeIndex));
		POINT pp[4];
		pp[0] = m_Editor.GetBench().BenchToClient(n.position);
		pp[1] = m_Editor.GetBench().BenchToClient(n.control[1]);
		pp[2] = m_Editor.GetBench().BenchToClient(m_Node.control[0]);
		pp[3] = m_Editor.GetBench().BenchToClient(m_Node.position);
		pDC->PolyBezier(pp, 4);
	}

	if (m_Curve.IsClosed() || m_NodeIndex < m_Curve.GetNodeCount() - 1) {
		// Draw the spline ahead
		BezierNode n = m_Curve.GetNode(Next(m_Curve, m_NodeIndex));
		POINT pp[4];
		pp[0] = m_Editor.GetBench().BenchToClient(m_Node.position);
		pp[1] = m_Editor.GetBench().BenchToClient(m_Node.control[1]);
		pp[2] = m_Editor.GetBench().BenchToClient(n.control[0]);
		pp[3] = m_Editor.GetBench().BenchToClient(n.position);
		pDC->PolyBezier(pp, 4);
	}

	m_Editor.DrawNode(pDC, m_Node, m_Curve.IsSelected(m_NodeIndex), RGB(0, 0, 255));
	m_Editor.DrawHandle(pDC, m_Node.position, m_Node.control[0], RGB(0, 0, 255));
	m_Editor.DrawHandle(pDC, m_Node.position, m_Node.control[1], RGB(0, 0, 255));

	pDC->SelectObject(pOld);
}

// DragNodes

DragNodes::DragNodes(BezierEditor & editor, CPoint start) :
	m_Editor(editor)
{
	for (Size i = 0; i < m_Editor.GetBezierOwner()->GetBezierCount(); ++i) {
		Bezier & b = m_Editor.GetBezierOwner()->GetBezier(i);
		for (Size j = 0; j < b.GetNodeCount(); ++j)
			if (b.IsSelected(j)) {
				BezierNode n = b.GetNode(j);
				m_Nodes.insert(std::make_pair(
					NodeIndex(&b, j), 
					NodePair(n, n)));
			}
	}
	m_Start = m_Editor.GetBench().ClientToBench(start);
	m_Editor.GetBench().SetCapture();
}

void DragNodes::Manipulate(UINT nFlags, CPoint p)
{
	NodeMap::iterator it;
	for (it = m_Nodes.begin(); it != m_Nodes.end(); ++it) {
		NodePair & n = (* it).second;
		n.first.position = 
			n.second.position + 
			m_Editor.GetBench().ClientToBench(p) - 
			m_Start;
		n.first.control[0] = n.first.position + n.second.control[0] - n.second.position;
		n.first.control[1] = n.first.position + n.second.control[1] - n.second.position;
	}
	m_Editor.GetBench().Invalidate();
}

void DragNodes::Apply(UINT nFlags, CPoint p)
{
	{
		My::MultiAction move("Move");
		NodeMap::iterator it;
		for (it = m_Nodes.begin(); it != m_Nodes.end(); ++it) {
			NodeIndex ni = (* it).first;
			ni.first->SetNode(ni.second, (* it).second.first);
		}
	}
	m_Editor.GetBench().Invalidate();
	ReleaseCapture();
}

void DragNodes::PaintRect(CDC * pDC, const CRect &)
{
	CPen blue(PS_SOLID, 1, RGB(0, 0, 255));
	CPen * pOld = pDC->SelectObject(&blue);

	Size i, j;

	// Iterate through nodes
	for (i = 0; i < m_Editor.GetBezierOwner()->GetBezierCount(); ++i) {
		Bezier & b = m_Editor.GetBezierOwner()->GetBezier(i);
		bool handles = !b.IsSelected() || b.GetNodeCount() == 1;
		for (j = 0; j < b.GetNodeCount(); ++j)
			if (b.IsSelected(j)) {
				BezierNode n = m_Nodes[NodeIndex(&b, j)].first;
				m_Editor.DrawNode(pDC, n, true, RGB(0, 0, 255));
				if (handles) {
					m_Editor.DrawHandle(pDC, n.position, n.control[0], RGB(0, 0, 255));
					m_Editor.DrawHandle(pDC, n.position, n.control[1], RGB(0, 0, 255));
				}
			}
	}

	// Iterate through curves
	for (i = 0; i < m_Editor.GetBezierOwner()->GetBezierCount(); ++i) {
		Bezier & b = m_Editor.GetBezierOwner()->GetBezier(i);
		bool handles = !b.IsSelected() || b.GetNodeCount() == 1;
		Size last = b.IsClosed() ? b.GetNodeCount() : b.GetNodeCount() - 1;
		for (Size j0 = 0; j0 < last; ++j0) {
			Size j1 = Next(b, j0);
			bool s0 = b.IsSelected(j0);
			bool s1 = b.IsSelected(j1);
			if (s0 || s1) {
				BezierNode n0 = s0 ? m_Nodes[NodeIndex(&b, j0)].first : b.GetNode(j0);
				BezierNode n1 = s1 ? m_Nodes[NodeIndex(&b, j1)].first : b.GetNode(j1);
				POINT pp[4];
				pp[0] = m_Editor.GetBench().BenchToClient(n0.position);
				pp[1] = m_Editor.GetBench().BenchToClient(n0.control[1]);
				pp[2] = m_Editor.GetBench().BenchToClient(n1.control[0]);
				pp[3] = m_Editor.GetBench().BenchToClient(n1.position);
				pDC->PolyBezier(pp, 4);
				if (!s0)
					m_Editor.DrawNode(pDC, n0, false, RGB(0, 0, 255));
				if (!s1)
					m_Editor.DrawNode(pDC, n1, false, RGB(0, 0, 255));
				if (handles) {
					if (!s0)
						m_Editor.DrawHandle(pDC, n0.position, n0.control[1], RGB(0, 0, 255));
					if (!s1)
						m_Editor.DrawHandle(pDC, n1.position, n1.control[0], RGB(0, 0, 255));
				}
			}
		}
	}
	pDC->SelectObject(pOld);
}

// DragCurve

DragCurve::DragCurve(BezierEditor & e, Bezier & b, float t, CPoint start) :
	m_Editor(e),
	m_Curve(b)
{
	assert(t > 0);
	if (m_Curve.IsClosed())
		assert(t < m_Curve.GetNodeCount());
	else
		assert(t < m_Curve.GetNodeCount() - 1);
	float fi = floor(t);
	m_Indexes[0] = Size(fi);
	m_Indexes[1] = (m_Indexes[0] + 1) % m_Curve.GetNodeCount();
	m_Nodes[0] = m_Curve.GetNode(m_Indexes[0]);
	if (m_Nodes[0].type == bnFlat)
		m_Nodes[0].type = bnSmooth;
	m_Nodes[1] = m_Curve.GetNode(m_Indexes[1]);
	if (m_Nodes[1].type == bnFlat)
		m_Nodes[1].type = bnSmooth;
	m_Controls[0] = m_Nodes[0].control[1];
	m_Controls[1] = m_Nodes[1].control[0];
	t -= fi;
	float d = 3 * (1 - t) * (1 - t) * (1 - t) * t + 3 * (1 - t) * t * t * t;
	m_Coefs[0] = (1 - t) / d;
	m_Coefs[1] = t / d;
	m_Start = m_Editor.GetBench().ClientToBench(start);
	m_Editor.GetBench().SetCapture();
}

void DragCurve::Manipulate(UINT flags, CPoint p)
{
	point<float> dp = m_Editor.GetBench().ClientToBench(p) - m_Start;
	SetNodeControlPoint(m_Nodes[0], 1, m_Controls[0] + dp * m_Coefs[0]);
	SetNodeControlPoint(m_Nodes[1], 0, m_Controls[1] + dp * m_Coefs[1]);
	m_Editor.GetBench().Invalidate();
}

void DragCurve::Apply(UINT flags, CPoint p)
{
	{
		My::MultiAction edit("Edit Curve");
		m_Curve.SetNode(m_Indexes[0], m_Nodes[0]);
		m_Curve.SetNode(m_Indexes[1], m_Nodes[1]);
	}
	m_Editor.GetBench().Invalidate();
	ReleaseCapture();
}

void DragCurve::PaintRect(CDC * pDC, const CRect &)
{
	CPen blue(PS_SOLID, 1, RGB(0, 0, 255));
	CPen * pOld = pDC->SelectObject(&blue);

	// Draw the spline back
	if (m_Curve.IsClosed() || m_Indexes[0] > 0) {
		BezierNode n = m_Curve.GetNode(Prev(m_Curve, m_Indexes[0]));
		POINT pp[4];
		pp[0] = m_Editor.GetBench().BenchToClient(n.position);
		pp[1] = m_Editor.GetBench().BenchToClient(n.control[1]);
		pp[2] = m_Editor.GetBench().BenchToClient(m_Nodes[0].control[0]);
		pp[3] = m_Editor.GetBench().BenchToClient(m_Nodes[0].position);
		pDC->PolyBezier(pp, 4);
	}

	POINT pp[4];
	pp[0] = m_Editor.GetBench().BenchToClient(m_Nodes[0].position);
	pp[1] = m_Editor.GetBench().BenchToClient(m_Nodes[0].control[1]);
	pp[2] = m_Editor.GetBench().BenchToClient(m_Nodes[1].control[0]);
	pp[3] = m_Editor.GetBench().BenchToClient(m_Nodes[1].position);
	pDC->PolyBezier(pp, 4);

	// Draw the spline ahead
	if (m_Curve.IsClosed() || m_Indexes[1] < m_Curve.GetNodeCount() - 1) {
		BezierNode n = m_Curve.GetNode(Next(m_Curve, m_Indexes[1]));
		POINT pp[4];
		pp[0] = m_Editor.GetBench().BenchToClient(m_Nodes[1].position);
		pp[1] = m_Editor.GetBench().BenchToClient(m_Nodes[1].control[1]);
		pp[2] = m_Editor.GetBench().BenchToClient(n.control[0]);
		pp[3] = m_Editor.GetBench().BenchToClient(n.position);
		pDC->PolyBezier(pp, 4);
	}

	m_Editor.DrawNode(pDC, m_Nodes[0], m_Curve.IsSelected(m_Indexes[0]), RGB(0, 0, 255));
	m_Editor.DrawNode(pDC, m_Nodes[1], m_Curve.IsSelected(m_Indexes[1]), RGB(0, 0, 255));

	m_Editor.DrawHandle(pDC, m_Nodes[0].position, m_Nodes[0].control[0], RGB(0, 0, 255));
	m_Editor.DrawHandle(pDC, m_Nodes[0].position, m_Nodes[0].control[1], RGB(0, 0, 255));
	m_Editor.DrawHandle(pDC, m_Nodes[1].position, m_Nodes[1].control[0], RGB(0, 0, 255));
	m_Editor.DrawHandle(pDC, m_Nodes[1].position, m_Nodes[1].control[1], RGB(0, 0, 255));

	pDC->SelectObject(pOld);
}

// SelectRange

SelectRange::SelectRange(BezierEditor & editor, CPoint p) :
	m_Editor(editor)
{
	m_Start = m_Editor.GetBench().ClientToBench(p);
	m_Range = rect<float>(m_Start, m_Start);

	CClientDC dc(&m_Editor.GetBench());
	CRect r;

	r = m_Editor.GetBench().BenchToClient(m_Range);
	dc.DrawFocusRect(&r);

	m_Editor.GetBench().SetCapture();
}

void SelectRange::Manipulate(UINT nFlags, CPoint p) 
{
	CClientDC dc(&m_Editor.GetBench());
	CRect r;

	r = m_Editor.GetBench().BenchToClient(m_Range);
	dc.DrawFocusRect(&r);

	m_Range = rect<float>(m_Start, m_Start);
	m_Range.include2(m_Editor.GetBench().ClientToBench(p));

	r = m_Editor.GetBench().BenchToClient(m_Range);
	dc.DrawFocusRect(&r);
	
	//m_Editor.GetBench().Invalidate();
}

void SelectRange::Apply(UINT nFlags, CPoint p) 
{
	m_Range = rect<float>(m_Start, m_Start);
	m_Range.include2(m_Editor.GetBench().ClientToBench(p));
	if ((GetAsyncKeyState(VK_SHIFT) & (~0 << 1)) == 0)
		m_Editor.GetBezierOwner()->DeselectAll();
	for (Size i = 0; i < m_Editor.GetBezierOwner()->GetBezierCount(); ++i) {
		Bezier & b = m_Editor.GetBezierOwner()->GetBezier(i);
		for (Size j = 0; j < b.GetNodeCount(); ++j)
			if (m_Range.inside(b.GetNode(j).position)) {
				if (b.IsSelected(j))
					b.Deselect(j);
				else
					b.Select(j);
			}
	}
	m_Editor.GetBench().Invalidate();
	ReleaseCapture();
}

void SelectRange::PaintRect(CDC * pDC, const CRect &) 
{
	CClientDC dc(&m_Editor.GetBench());
	CRect r;
	r = m_Editor.GetBench().BenchToClient(m_Range);
	dc.DrawFocusRect(&r);
}

} // My